package com.diaohw.module.metadata.service.meta;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.diaohw.module.metadata.enums.ItemTypeEnums;
import com.diaohw.module.metadata.mapper.*;
import com.diaohw.module.metadata.obj.entity.*;
import com.diaohw.module.metadata.obj.query.MetaDataQuery;
import com.diaohw.module.metadata.obj.vo.MetaDataVO;
import com.diaohw.module.metadata.service.MetaDataService;
import com.diaohw.platform.common.enums.CodeMsgEnum;
import com.diaohw.platform.common.obj.page.PageResultVo;
import com.diaohw.platform.common.util.JsonUtil;
import com.diaohw.platform.framework.db.utils.WrapperUtils;
import com.diaohw.platform.remote.metadata.obj.bo.ParamBO;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 元数据属性值 服务实现类
 * </p>
 *
 * @author D-Hw
 * @since 2025-04-07
 */
@Service
public class MetaDataImpl extends ServiceImpl<MetaDataMapper, MetaDataDO> implements MetaDataService {


    @Resource
    private MetaValMapper valMapper;

    @Resource
    private MetaMapper metaMapper;
    @Resource
    private MetaItemMapper itemMapper;
    @Resource
    private MetaRelationshipMapper relationMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean create(ParamBO param) {
        List<String> rowIds = new ArrayList<>();
        if (Boolean.TRUE.equals(param.getGroup())) {
            rowIds.addAll(group(param));
        }
        String rowId = IdUtil.getSnowflakeNextIdStr();
        List<MetaDataDO> execute = serialization(param.getMetaId(), param.getDataId(), rowId, 1, JsonUtil.toJsonNode(param.getValue()));
        rowIds.add(rowId);
        this.saveBatch(execute);
        MetaValDO source = new MetaValDO();
        source.setId(param.getDataId());
        source.setTargetId(param.getMetaId());
        source.setGroup(param.getGroup());
        source.setRowId(rowIds);
        source.setVal(param.getValue());
        valMapper.insert(source);
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(ParamBO param) {
        delete(param.getDataId());
        return create(param);
    }

    @Override
    public Boolean delete(String id) {
        valMapper.deleteById(id);
        this.remove(Wrappers.<MetaDataDO>lambdaQuery().eq(MetaDataDO::getDataId, id));
        return Boolean.TRUE;
    }

    @Override
    public MetaDataVO detail(String id) {
        MetaDataDO model = this.getById(id);
        return INSTANCE.entityToVo(model);
    }

    @Override
    public List<MetaDataVO> findByList(MetaDataQuery query) {
        LambdaQueryWrapper<MetaDataDO> wrapper = createWrapper(query);
        List<MetaDataDO> list = this.list(wrapper);
        return INSTANCE.entityToVo(list);
    }

    @Override
    public PageResultVo<MetaDataVO> findByPage(MetaDataQuery query) {
        LambdaQueryWrapper<MetaDataDO> wrapper = createWrapper(query);
        Page<MetaDataDO> page = this.page(WrapperUtils.createPage(query), wrapper);
        List<MetaDataVO> list = INSTANCE.entityToVo(page.getRecords());
        return PageResultVo.create(page.getTotal(), list);
    }

    private List<MetaItemDO> items(String metaId) {
        return itemMapper.selectList(Wrappers.<MetaItemDO>lambdaQuery().in(MetaItemDO::getMetaId, metaId));
    }

    @Transactional(rollbackFor = Exception.class)
    public List<String> group(ParamBO param) {

        JsonNode node = JsonUtil.toJsonNode(param.getValue());
        List<MetaRelationshipDO> relationships = relationMapper.selectList(Wrappers.<MetaRelationshipDO>lambdaQuery().eq(MetaRelationshipDO::getGroupId, param.getMetaId()));
        List<String> rowIds = new ArrayList<>();
        List<MetaDataDO> rows = new ArrayList<>();
        for (MetaRelationshipDO relationship : relationships) {

            JsonNode value = node.get(relationship.getMetaCode());
            JsonNodeType nodeType = value.getNodeType();
            if (nodeType != JsonNodeType.OBJECT && nodeType != JsonNodeType.ARRAY) {
                CodeMsgEnum.DATA.throwException(String.format("[%s]输入有误", relationship.getMetaCode()));
            }

            if (nodeType == JsonNodeType.OBJECT) {
                JsonNode jsonNode = value.get(relationship.getMetaCode());
                String id = IdUtil.getSnowflakeNextIdStr();
                rowIds.add(id);
                rows.addAll(serialization(relationship.getMetaId(), param.getDataId(), id, 1, jsonNode));
                continue;
            }

            for (JsonNode next : node) {
                String id = IdUtil.getSnowflakeNextIdStr();
                rowIds.add(id);
                rows.addAll(serialization(relationship.getMetaId(), param.getDataId(), id, 1, next));
            }
        }

        this.saveBatch(rows);

        return rowIds;

    }

    private List<MetaDataDO> serialization(String metaId, String dataId, String rowId, int level, JsonNode data) {
        List<MetaItemDO> items = items(metaId);
        MetaDO meta = metaMapper.selectById(metaId);
        List<MetaDataDO> list = new ArrayList<>();
        items.forEach(item -> {

            MetaDataDO value = MetaDataDO.of(dataId, rowId, level, metaId, meta.getCode(), item.getId(), item.getItemCode());

            list.add(value);

            if (!data.has(item.getItemCode())) {
                value.setVal(item.getDefaultVal());
                return;
            }

            JsonNode object = data.get(item.getItemCode());
            if (item.getDataType() != ItemTypeEnums.OBJECT) {
                value.setVal(JsonUtil.toJsonString(object));
                return;
            }
            JsonNode node = JsonUtil.toJsonNode(object);
            JsonNodeType nodeType = node.getNodeType();
            if (Boolean.TRUE.equals(item.getMultiple())) {
                if (nodeType != JsonNodeType.ARRAY) {
                    CodeMsgEnum.DATA.throwException(String.format("[%s]输入有误", item.getItemCode()));
                }
                List<String> ids = new ArrayList<>();
                for (JsonNode next : node) {
                    String id = IdUtil.getSnowflakeNextIdStr();
                    List<MetaDataDO> execute = serialization(item.getDefaultVal(), dataId, id, level + 1, next);
                    list.addAll(execute);
                    ids.add(id);
                }
                value.setVal(JsonUtil.toJsonString(ids));
                return;
            }

            if (nodeType != JsonNodeType.OBJECT) {
                CodeMsgEnum.DATA.throwException(String.format("[%s]输入有误", item.getItemCode()));
            }
            String id = IdUtil.getSnowflakeNextIdStr();
            List<MetaDataDO> execute = serialization(item.getDefaultVal(), dataId, id, level + 1, node);
            value.setVal(id);
            list.addAll(execute);
        });

        return list;
    }

    /**
     * 生成查询条件
     *
     * @param query 查询条件
     */
    private LambdaQueryWrapper<MetaDataDO> createWrapper(MetaDataQuery query) {
        LambdaQueryWrapper<MetaDataDO> wrapper = Wrappers.lambdaQuery(MetaDataDO.class);
        wrapper.like(StrUtil.isNotBlank(query.getDataId()), MetaDataDO::getDataId, query.getDataId());
        wrapper.like(StrUtil.isNotBlank(query.getRowId()), MetaDataDO::getRowId, query.getRowId());
        wrapper.like(StrUtil.isNotBlank(query.getMetaId()), MetaDataDO::getMetaId, query.getMetaId());
        wrapper.like(StrUtil.isNotBlank(query.getMetaCode()), MetaDataDO::getMetaCode, query.getMetaCode());
        wrapper.like(StrUtil.isNotBlank(query.getItemId()), MetaDataDO::getItemId, query.getItemId());
        wrapper.like(StrUtil.isNotBlank(query.getItemCode()), MetaDataDO::getItemCode, query.getItemCode());
        wrapper.like(StrUtil.isNotBlank(query.getVal()), MetaDataDO::getVal, query.getVal());
        return wrapper;
    }

}
